/*-------------------------------------------------------------------------
 *
 * pg_conversion.c
 *	  支持pg_conversion关系操作的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_conversion.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/syscache.h"

/*
 * ConversionCreate
 *
 * 向 pg_conversion 中添加新元组。
 */
ObjectAddress ConversionCreate(const char *fc_conname, Oid fc_connamespace,
				 Oid fc_conowner,
				 int32 fc_conforencoding, int32 fc_contoencoding,
				 Oid fc_conproc, bool fc_def)
{
	int			fc_i;
	Relation	fc_rel;
	TupleDesc	fc_tupDesc;
	HeapTuple	fc_tup;
	Oid			fc_oid;
	bool		fc_nulls[Natts_pg_conversion];
	Datum		fc_values[Natts_pg_conversion];
	NameData	fc_cname;
	ObjectAddress fc_myself,
				fc_referenced;

	/* 运行检查 */
	if (!fc_conname)
		elog(ERROR, "no conversion name supplied");

	/* 确保没有同名的现有转换 */
	if (SearchSysCacheExists2(CONNAMENSP,
							  PointerGetDatum(fc_conname),
							  ObjectIdGetDatum(fc_connamespace)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("conversion \"%s\" already exists", fc_conname)));

	if (fc_def)
	{
		/*
		 * 确保在此命名空间中没有现有的默认 <for encoding><to encoding>
		 * 对。
 */
		if (FindDefaultConversion(fc_connamespace,
								  fc_conforencoding,
								  fc_contoencoding))
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("default conversion for %s to %s already exists",
							pg_encoding_to_char(fc_conforencoding),
							pg_encoding_to_char(fc_contoencoding))));
	}

	/* 打开 pg_conversion */
	fc_rel = table_open(ConversionRelationId, RowExclusiveLock);
	fc_tupDesc = fc_rel->rd_att;

	/* 初始化 nulls 和值 */
	for (fc_i = 0; fc_i < Natts_pg_conversion; fc_i++)
	{
		fc_nulls[fc_i] = false;
		fc_values[fc_i] = (Datum) NULL;
	}

	 /* 形成一个元组 */
	namestrcpy(&fc_cname, fc_conname);
	fc_oid = GetNewOidWithIndex(fc_rel, ConversionOidIndexId,
							 Anum_pg_conversion_oid);
	fc_values[Anum_pg_conversion_oid - 1] = ObjectIdGetDatum(fc_oid);
	fc_values[Anum_pg_conversion_conname - 1] = NameGetDatum(&fc_cname);
	fc_values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(fc_connamespace);
	fc_values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(fc_conowner);
	fc_values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(fc_conforencoding);
	fc_values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(fc_contoencoding);
	fc_values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(fc_conproc);
	fc_values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(fc_def);

	fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);

	 /* 插入一个新元组 */
	CatalogTupleInsert(fc_rel, fc_tup);

	fc_myself.classId = ConversionRelationId;
	fc_myself.objectId = fc_oid;
	fc_myself.objectSubId = 0;

	/* 创建对转换过程的依赖 */
	fc_referenced.classId = ProcedureRelationId;
	fc_referenced.objectId = fc_conproc;
	fc_referenced.objectSubId = 0;
	recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

	 /* 创建对命名空间的依赖 */
	fc_referenced.classId = NamespaceRelationId;
	fc_referenced.objectId = fc_connamespace;
	fc_referenced.objectSubId = 0;
	recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

	 /* 创建对所有者的依赖 */
	recordDependencyOnOwner(ConversionRelationId, fc_oid, fc_conowner);

	/* 对扩展的依赖 */
	recordDependencyOnCurrentExtension(&fc_myself, false);

	/* 新转换的创建后钩子 */
	InvokeObjectPostCreateHook(ConversionRelationId, fc_oid, 0);

	heap_freetuple(fc_tup);
	table_close(fc_rel, RowExclusiveLock);

	return fc_myself;
}

/*
 * FindDefaultConversion
 *
 * 在给定命名空间中通过 for_encoding 和 to_encoding 查找“默认”转换过程。
 *
 * 如果找到，则返回该过程的 oid，否则返回 InvalidOid。请注意，您获得的是过程的 OID，而不是转换的 OID！
 */
Oid FindDefaultConversion(Oid fc_name_space, int32 fc_for_encoding, int32 fc_to_encoding)
{
	CatCList   *fc_catlist;
	HeapTuple	fc_tuple;
	Form_pg_conversion fc_body;
	Oid			fc_proc = InvalidOid;
	int			fc_i;

	fc_catlist = SearchSysCacheList3(CONDEFAULT,
								  ObjectIdGetDatum(fc_name_space),
								  Int32GetDatum(fc_for_encoding),
								  Int32GetDatum(fc_to_encoding));

	for (fc_i = 0; fc_i < fc_catlist->n_members; fc_i++)
	{
		fc_tuple = &fc_catlist->members[fc_i]->tuple;
		fc_body = (Form_pg_conversion) GETSTRUCT(fc_tuple);
		if (fc_body->condefault)
		{
			fc_proc = fc_body->conproc;
			break;
		}
	}
	ReleaseSysCacheList(fc_catlist);
	return fc_proc;
}
